package com.gframework.core.service.observed;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;

import com.gframework.annotation.ThreadSafe;
import com.gframework.context.spring.AppContext;
import com.gframework.lang.IntegerVar;
import com.gframework.util.ReflectUtils;

/**
 * 观察者与被观察者模型操作AOP.
 * 
 * <p>此AOP用于处理使用了{@link ObservedMethod}注解的观察者逻辑。
 * 
 * <p>本类不会捕捉并且会抛出所有异常。同时本类在执行过程中有一个TTL以防止循环调用问题的发生，如果调用深度过深，则会检测到并抛出异常。
 * 
 * <p>需要注意的是：本类通过{@link AppContext#getBeansOfType(Class)} 方法获取到bean之后会进行缓存，
 * 如果程序中动态对bean进行了加载或卸载，是无法得知的。
 * 
 * @since 1.0.0
 * @author Ghwolf
 *
 * @see ObservedMethod
 */
@Aspect
@Order(9999)
@ThreadSafe
public class ObservedAOP {
	
	/**
	 * 切入表达式
	 */
	private static final String AROUND_ASPECTJ = "@annotation(com.gframework.core.service.observed.ObservedMethod)";
	/**
	 * 调用最大深度：{@value}
	 */
	private static final int TTL = 32 ;
	/**
	 * 记录当前调用深度
	 */
	private static final ThreadLocal<IntegerVar> ttlLocal = new ThreadLocal<>();
	
	/***
	 * 观察者模型缓存集合
	 */
	private final Map<String,Observer> observerCache = new ConcurrentHashMap<>();
	
	public ObservedAOP(){
		LoggerFactory.getLogger(ObservedAOP.class).info("====> 已启用 ObservedAOP 观察者AOP！@ObservedMethod 注解绑定的方法将会与被观察者关联执行！");
	}
	
	/**
	 * 拦截方法
	 */
	@Around(AROUND_ASPECTJ)
	public Object around(ProceedingJoinPoint point) throws Throwable{
		Object[] args = point.getArgs();
		Object returnValue = point.proceed(args);
		
		MethodSignature sign = (MethodSignature) point.getSignature();
		Observer ob = this.observerCache.computeIfAbsent(point.toLongString(),k -> createObserver(sign.getMethod()));
		createTTLVarIfNull();
		try {
			ob.notice(args, sign);
		} catch(Throwable e) {
			ttlLocal.remove();
			throw e ;
		} finally {
			IntegerVar v = ttlLocal.get();
			if (v != null && v.intValue() <= 0) {
				ttlLocal.remove();
			}
		}
		
		return returnValue ;
	}
	
	private void createTTLVarIfNull() {
		IntegerVar var = ttlLocal.get();
		if (var == null) {
			ttlLocal.set(new IntegerVar(1));
		}
	}
	
	/**
	 * 根据method对象创建一个观察者对象.
	 * <p>如果配置错误，则抛出异常
	 * @param method 代理方法
	 * @return 返回观察者对象
	 * @throws IllegalArgumentException 如果配置错误
	 */
	private Observer createObserver(Method method) {
		ObservedMethod om = method.getAnnotation(ObservedMethod.class);
		Class<?> type = om.value();
		Method observerMethod = ReflectUtils.getFunctionInterfaceMethod(type);
		InvokeMethodHandler handler = null;
		if (observerMethod == null) {
			String methodName = om.method();
			if ("".equals(methodName)) {
				methodName = method.getName();
			}
			for (Method m : type.getMethods()) {
				if (m.getName().equals(methodName)) {
					InvokeMethodHandler h = getMethodHandler(method,m);
					if (h != null) {
						if (handler == null) {
							handler = h;
							observerMethod = m ;
						} else {
							throw new IllegalArgumentException(method.getDeclaringClass().getName() + "#" + method.getName() + " 方法上ObservedMethod注解的配置的类存在不止一个匹配的方法！"
										+ "观察者的方法参数要么无，要么只有MethodSignature，要么和注解方法参数及顺序一致，要么再有原始方法参数的基础上，最后再加上MethodSignature参数！");
						}
					}
				}
			}
		} else {
			handler = getMethodHandler(method,observerMethod);
		}
		if (handler == null) {
			throw new IllegalArgumentException(method.getDeclaringClass().getName() + "#" + method.getName() + " 方法上ObservedMethod注解的配置无法找到合适的观察者方法！"
					+ "观察者的方法参数要么无，要么只有MethodSignature，要么和注解方法参数及顺序一致，要么再有原始方法参数的基础上，最后再加上MethodSignature参数！");
		}
		Object[] observers ;
		Map<String,?> beans = AppContext.getBeansOfType(type);
		if (beans.isEmpty()) {
			observers = ArrayUtils.EMPTY_OBJECT_ARRAY;
		} else {
			observers = beans.values().toArray(new Object[beans.size()]);
		}
		return new Observer(observerMethod,observers,handler) ;
	}
	
	/**
	 * 取得观察者操作方法的执行对象.
	 * @param observedMethod 被观察的方法
	 * @param observerMethod 观察者的操作方法
	 */
	private InvokeMethodHandler getMethodHandler(Method observedMethod,Method observerMethod) {
		if (observerMethod.getParameterCount() == 0) {
			return NO_PARAM_HANDLER;
		}
		// 观察者操作方法参数
		Parameter[] observerParam = observerMethod.getParameters();
		if (observerParam.length == 1 && observerParam[0].getType() == MethodSignature.class) {
			return SIGN_PARAM_HANDLER;
		}
		// 被观察的方法参数
		Parameter[] observedParam = observedMethod.getParameters();
		if (observedParam.length == 0) {
			return null ;
		}
		if (paramEq(observedParam,observerParam,observedParam.length)) {
			if (observedParam.length == observerParam.length) {
				return ORIGINAL_PARAM_HANDLER;
			} else if (observerParam.length == observedParam.length + 1 && observerParam[observedParam.length].getType() == MethodSignature.class){
				return ORIGINAL_AND_SIGN_PARAM_HANDLER;
			}
		}
		return null ;
	}
	/**
	 * 比较两个方法前几位参数的类型是否一致
	 */
	private boolean paramEq(Parameter[] p1,Parameter[] p2,int length) {
		if (p1.length < length || p2.length < length) {
			return false ;
		}
		for (int x = 0 ; x < length ; x ++) {
			if (p1[x].getType() != p2[x].getType()) {
				return false ;
			}
		}
		return true ;
	}
	
	/**
	 * 观察者类.
	 * 
	 * @author Ghwolf
	 */
	private class Observer {
		/**
		 * 观察者执行的方法
		 */
		private Method method ;
		/**
		 * 观察者
		 */
		private Object[] observers ;
		/**
		 * 方法执行对象
		 */
		private InvokeMethodHandler handler ;
		
		Observer(Method method,Object[] observers,InvokeMethodHandler handler) {
			this.method = method ;
			this.method.setAccessible(true);
			this.observers = observers ;
			this.handler = handler ;
		}
		
		/**
		 * 通知所有观察者执行方法
		 * @throws Exception 方法执行异常
		 */
		public void notice(Object[] args,MethodSignature sign) throws Throwable {
			IntegerVar i = ttlLocal.get();
			if (i.intValue() >= TTL) {
				throw new StackOverflowError("观察者循环调用 【严重错误】，请检查所有的ObservedMethod注解配置，是否存在循环调用问题，目前调用深度已经到达 " + TTL
						+ " 次，如果没有循环调用，而是调用就是很深，那么可以尝试增加ObservedAOP#TTL的值。");
			}
			for (Object ob : this.observers) {
				i.add();
				this.handler.invoke(this.method, ob, args, sign);
				i.sub();
			}
		}
		
	}
	
	/**
	 * 方法执行逻辑，根据方法参数形式的不同，给与不同的参数传递。
	 * 
	 * @author Ghwolf
	 *
	 */
	@FunctionalInterface
	private static interface InvokeMethodHandler {
		/**
		 * 调用方法
		 * @throws Exception 方法执行异常
		 */
		public void invoke(Method method,Object obj,Object[] args,MethodSignature sign) throws Exception ;
	}
	/**
	 * 无参数的方法执行逻辑
	 */
	private static final InvokeMethodHandler NO_PARAM_HANDLER = (m,o,args,s) -> m.invoke(o);
	/**
	 * 仅只有MethodSignature一个参数的方法执行逻辑
	 */
	private static final InvokeMethodHandler SIGN_PARAM_HANDLER = (m,o,args,s) -> m.invoke(o,s);
	/**
	 * 包含原始所有参数且顺序相同，但是不包含MethodSignature参数的方法执行逻辑
	 */
	private static final InvokeMethodHandler ORIGINAL_PARAM_HANDLER = (m,o,args,s) -> m.invoke(o,args);
	/**
	 * 包含原始所有参数且顺序相同，最后还有个MethodSignature参数的方法执行逻辑
	 */
	private static final InvokeMethodHandler ORIGINAL_AND_SIGN_PARAM_HANDLER = (m,o,args,s) -> m.invoke(o,newArray(args,s));
	
	private static final Object[] newArray(Object[] arr,Object obj) {
		Object[] newArr = new Object[arr.length + 1];
		System.arraycopy(arr, 0, newArr, 0, arr.length);
		newArr[arr.length] = obj ;
		return newArr;
	}

}
